import type { PixelPoint } from "../commonTypes"
import { Camera } from "../utils/cameraUtil"
import { Sphere } from "../utils/geometryUtil"
import { HitList, type Hitable } from "../utils/hitUtil"
import Lambertian, { Metal } from "../utils/materialUtil"
import { Ray } from "../utils/rayUtil"
import { smoothColorRound } from "../utils/smoothUtil"
import { add, dot, elemMul, Vec3 } from "../utils/vectorUtil"

/** 定义相机的位置和屏幕的位置 */
const camera = new Camera(
  // 相机位置
  new Vec3(0, 0, 1), //origin
  // 屏幕位置
  new Vec3(-2, -1, -1), //leftBottom
  // 屏幕方向向量
  new Vec3(4, 0, 0), //horizontal
  new Vec3(0, 2, 0), //vertical
)

/** 设置多个球体和世界 */
const sphere1 = new Sphere(new Vec3(0, 0, -1), 0.5, new Metal(new Vec3(0.8, 0.4, 0))) // 橙
const sphere2 = new Sphere(new Vec3(1, 0, -1), 0.5, new Metal(new Vec3(0.8, 0, 0.4))) // 紫
const sphere3 = new Sphere(new Vec3(-1, 0, -1), 0.5, new Metal(new Vec3(0.4, 0.8, 0))) // 黄绿
const earth = new Sphere(new Vec3(0, -100.5, -1), 100, new Metal(new Vec3(0.5, 0.5, 0.5))) // 灰色
const world = new HitList(
  [sphere1, sphere2, sphere3, earth]
)

/** 设置多个球体和世界 */
const sphere4 = new Sphere(new Vec3(0, 0, -1), 0.5, new Metal(new Vec3(0.8, 0.4, 0), 0), 'orange') // 橙
const sphere5 = new Sphere(new Vec3(1, 0, -1), 0.5, new Metal(new Vec3(0.8, 0, 0.4), 1), 'purple') // 紫
const sphere6 = new Sphere(new Vec3(-1, 0, -1), 0.5, new Lambertian(new Vec3(0.4, 0.8, 0)), 'green') // 黄绿
const earth2 = new Sphere(new Vec3(0, -100.5, -1), 100, new Metal(new Vec3(0.5, 0.5, 0.5), 0.2), 'earth') // 灰色
const world2 = new HitList(
  [sphere4, sphere5, sphere6, earth2]
)


/**
 * 渲染每个像素
 * @param px 
 * @param width 
 * @param height 
 */
export function renderPixelMaterial(
  px: PixelPoint,
  width: number,
  height: number) {
  // 解析颜色和透明度，并进行解构赋值
  [px.r, px.g, px.b, px.a] = [...smoothColor(px, width, height, world), 1]
    .map(px => {
      // 转换0-1的颜色到0-255范围内
      return Math.floor(px * 255.9)
    })
}

/**
 * 渲染每个像素
 * @param px 
 * @param width 
 * @param height 
 */
export function renderPixelMaterialFuzz(
  px: PixelPoint,
  width: number,
  height: number) {
  // 解析颜色和透明度，并进行解构赋值
  [px.r, px.g, px.b, px.a] = [...smoothColor(px, width, height, world2), 1]
    .map(px => {
      // 转换0-1的颜色到0-255范围内
      return Math.floor(px * 255.9)
    })
}


/**
 * 着色函数
 * @param _x 水平位置比例
 * @param _y 垂直位置比例
 * @param hitable
 * @returns 
 */
function color(_x: number, _y: number, hitable: Hitable) {
  // 将y轴转换为从低到高的数据
  const [x, y] = [_x, 1 - _y]
  // if (Math.abs(x - 0.5) > 1.0E-6 || Math.abs(y - 0.25) > 1.0E-6) {
  //   return [1, 1, 1]
  // }
  // console.log('color', x, y)

  // 获取相机到屏幕的光线
  const ray = camera.getRay(x, y)

  // 光线追踪
  const resColor = trace(hitable, ray)

  return [resColor.e0, resColor.e1, resColor.e2]
}

function smoothColor(px: PixelPoint, width: number, height: number,
  hitable: Hitable) {
  // return smoothColorNone(px, width, height, hitable, color)
  // return smoothColorRandom(px, width, height, hitable, color)
  return smoothColorRound(px, width, height, hitable, color)
}

/**
 * 获得渐变背景颜色
 * @param ray 
 * @returns 
 */
function backgroundColor(ray: Ray) {
  // 获得光线的单位方向向量
  const unitDirection = ray.direction.unitVec()
  // 获得光线方向 e1垂直 方向上的百分比，并减半缩减到0-1的范围之内
  const colorPercent = (unitDirection.e1 + 1.0) * 0.5
  // 白颜色强度
  const whiteColor = new Vec3(1, 1, 1).mul(1 - colorPercent)
  // 蓝颜色的强度
  const blueColor = new Vec3(0.3, 0.5, 1).mul(colorPercent)
  // const blueColor = new Vec3(1, 0, 0).mul(colorPercent)
  // 混合蓝色和白色设置为背景色，得到白色到蓝色的渐变
  const resColor = add(whiteColor, blueColor)
  return resColor
}

/**
 * 光线追踪
 * @param hitable 整个场景
 * @param ray 当前的光线
 * @param attenuation 衰减
 * @param step 递归的阶次
 * @return 返回颜色
 */
function trace(hitable: Hitable, ray: Ray, step = 0): Vec3 {

  // 递归超过50次返回黑色
  if (step > 50) {
    return new Vec3(0, 0, 0)
  }

  // 计算交互结果
  const hitResult = hitable.hit(ray, 0.0000001, Infinity)


  // 返回的颜色
  let resColor: Vec3
  if (hitResult) {
    // 增加衰减
    if (hitResult.isDiffusion) {
      // 获得法线对应的背景色
      let bg = backgroundColor(new Ray(new Vec3(0, 0, 0), hitResult.hit.normal))
      // 获得法线与屏幕指向光源的亮度衰减
      let attenuation = Math.max(0, dot(new Vec3(0, 0, 1), hitResult.hit.normal))
      attenuation = 1 - ((1 - attenuation) * 0.6)
      // 着色并衰减颜色
      resColor = elemMul(bg, hitResult.attenuation).mul(attenuation)
    } else {
      let tmpColor = trace(hitable, hitResult.reflectRay, ++step)
      resColor = elemMul(tmpColor, hitResult.attenuation)
    }
  } else {
    // 设置背景色
    resColor = backgroundColor(ray)
  }
  // console.log('trace', step, JSON.stringify(ray))
  // console.log('hitresult', JSON.stringify(hitResult))
  // console.log('resColor', resColor)

  return resColor
}

